package core

import (
	"bytes"
	"github.com/gorilla/websocket"
	jsoniter "github.com/json-iterator/go"
	"log"
	"net/http"
	"reflect"
	"sync"
	"time"
)

const (
	writeWait = 10 * time.Second

	// Time allowed to read the next pong message from the peer.
	pongWait = 60 * time.Second

	// Send pings to peer with this period. Must be less than pongWait.
	pingPeriod = (pongWait * 9) / 10

	maxMessageSize = 16 * 1024 * 1024
)

var upgrader = websocket.Upgrader{
	ReadBufferSize:   1024,
	WriteBufferSize:  1024,
	HandshakeTimeout: 5 * time.Second,
	CheckOrigin: func(r *http.Request) bool {
		return true
	},
}
var clients = map[*websocket.Conn]bool{}
var socketMutex sync.RWMutex

type WsConn struct {
	closed bool
	ws     *websocket.Conn
	//closeCh chan bool
	mutex  sync.Mutex
	inCh   chan *WsMessage //io.Reader
	outCh  chan *WsMessage
	exitCh chan bool
}

func (t *WsConn) close() {
	t.mutex.Lock()
	defer t.mutex.Unlock()
	if t.closed {
		return
	}
	t.closed = true
	t.ws.Close()
}

type WsMessage struct {
	msgType int
	content []byte
}

func WebsocketHandler(w http.ResponseWriter, r *http.Request) {
	ws, err := upgrader.Upgrade(w, r, nil)
	//ws.SetReadLimit()
	if err != nil {
		return
	}
	//socketMutex.Lock()
	//defer socketMutex.Unlock()
	//clients = append(clients, ws)
	//clients[ws] = true
	var conn = &WsConn{
		closed: false,
		ws:     ws,
		inCh:   make(chan *WsMessage, 1024),
		outCh:  make(chan *WsMessage, 1024),
		exitCh: make(chan bool, 1),
	}

	go wsCallbackHandler(conn)
	go wsWriteHandler(conn)
	go wsReadHandler(conn)

	//go func() {
	//	//defer ws.Close()
	//	for {
	//		a,b,err := ws.ReadMessage()
	//		log.Println(a,b,err)
	//		if err != nil {
	//			return
	//		}
	//
	//		select {
	//		}
	//	}
	//}()
	//
	//go func() {
	//
	//}()

	//go func() {
	//	defer ws.Close()
	//}()
	//
	//go func() {
	//	defer ws.Close()
	//}()
}

func wsCallbackHandler(conn *WsConn) {
	defer conn.close()
	for {
		select {
		case msg := <-conn.inCh:
			//event
			var i0 = bytes.IndexByte(msg.content, ' ')
			if i0 == -1 {
				break
			}
			var event = string(msg.content[:i0])
			var i1 = bytes.IndexByte(msg.content[i0+1:], ' ')
			if i1 == -1 {
				break
			}
			var ack = msg.content[i0+1 : i0+1+i1]
			var fun = events[event]
			if fun != nil {
				var ret, err = fun(conn, msg.content[i0+i1+1:])
				if err != nil {
					log.Println(err.Error())
					break
				}
				if ret != nil {
					var cnt, err = jsoniter.Marshal(ret)
					if err != nil {
						break
					}
					//附加ack信息
					msg.content = append([]byte{}, []byte("ack ")...)
					msg.content = append(msg.content, []byte(ack)...)
					msg.content = append(msg.content, ' ')
					msg.content = append(msg.content, cnt...)
					conn.outCh <- msg
				}
			}
			break
		case <-conn.exitCh:
			conn.exitCh <- true
			return
		}
	}
}

func wsWriteHandler(conn *WsConn) {
	defer conn.close()
	ticker := time.NewTicker(pingPeriod)
	defer ticker.Stop()
	for {
		select {
		case msg := <-conn.outCh:
			// 写给websocket
			var err = conn.ws.WriteMessage(msg.msgType, msg.content)
			if err != nil {
				return
			}
			break
		case <-ticker.C:
			// 出现超时情况
			var err = conn.ws.SetWriteDeadline(time.Now().Add(writeWait))
			if err != nil {
				return
			}
			if err := conn.ws.WriteMessage(websocket.PingMessage, nil); err != nil {
				return
			}

		case <-conn.exitCh:
			conn.exitCh <- true
			return
		}
	}
}

func wsReadHandler(conn *WsConn) {
	defer conn.close()
	conn.ws.SetReadLimit(maxMessageSize)
	var err = conn.ws.SetReadDeadline(time.Now().Add(pongWait))
	if err != nil {
		return
	}
	for {
		msgType, msg, err := conn.ws.ReadMessage()
		if err != nil {
			return
		}
		select {
		case conn.inCh <- &WsMessage{
			msgType: msgType,
			content: msg,
		}:
			break
		case <-conn.exitCh:
			conn.exitCh <- true
			return
		}
	}
}

var events = map[string]func(conn *WsConn, bs []byte) (interface{}, error){}

func RegisterEvent(event string, callback interface{}) bool {
	var v = reflect.ValueOf(callback)
	if v.Kind() != reflect.Func {
		return false //errors.New("RegisterEvent 错误的方法注册")
	}

	var tp = v.Type()
	var args []reflect.Type
	for i := 0; i < tp.NumIn(); i++ {
		args = append(args, tp.In(i).Elem())
	}
	events[event] = func(conn *WsConn, bs []byte) (value interface{}, err error) {
		//做出结构
		var arguments = []reflect.Value{}
		for i, vv := range args {
			if i == 0 {
				arguments = append(arguments, reflect.ValueOf(conn))
			} else {
				ins := reflect.New(vv).Interface()
				err = jsoniter.Unmarshal(bs, ins)
				if err != nil {
					return
				}
				arguments = append(arguments, reflect.ValueOf(ins))
			}
		}
		var ret = v.Call(arguments)
		return ret[0].Interface(), nil
	}
	return true
}
